﻿using System;
using mediaPrime.Logger;
using mediaPrime.Sdk;

namespace mediaPrime.Core
{
    /// <summary>
    /// This class handles all Messages (mediaPrime.Sdk.Message)
    /// </summary>
    public class MessageHandler
    {
        private MainWindow mainWindow;

        public MessageHandler(MainWindow mainWindow)
        {
            this.mainWindow = mainWindow;
        }

        public void HandleMessage(Message message)
        {
            if (message == null || message.Content == null)
            {
                Log.Error("Message or its content was null!");
                return;
            }

            // Check if receiver is mediaPrime or a Plugin
            if (message.Receiver != MessageReceivers.mediaPrime)
            {
                ForwardMessage(message);
                return;
            }

            // Check message type
            if (message.Type == MessageTypes.Command)
            {
                HandleCommand(message);
                return;
            }
            else if (message.Type == MessageTypes.Notification)
            {
                HandleNotification(message);
                return;
            }
            else if (message.Type == MessageTypes.Query)
            {
                HandleQuery(message);
                return;
            }
            else
            {

            }
        }

        public object HandleMessageWithResult(Message message)
        {
            return mainWindow.PluginManager.SendMessageWithResult(message);
        }

        public void HandleCommand(Message message)
        {

        }

        public void HandleQuery(Message message)
        {
            var query = message.Content as Query;

            if (query != null)
            {
                if (query.Type == QueryTypes.mediaPrimeVersion)
                    SendQueryResult(App.Version, message);
                else if (query.Type == QueryTypes.AvailablePlugins)
                {
                    // Request only allowed from internal plugins
                    if (message.Sender == "mediaPrime.Plugin.System")
                        SendQueryResult(mainWindow.PluginManager.AllAvailableExtensions, message);
                    else
                        SendQueryResultError(new UnauthorizedAccessException("You are not allowed to access this collection!"), message);
                }
            }
        }

        public void HandleNotification(Message message)
        {

        }

        public void ForwardMessage(Message message)
        {
            mainWindow.PluginManager.SendMessage(message);
        }

        public void SendQueryResultError(Exception exception, Message message)
        {
            var resultMessage = new Message();
            var queryResult = new QueryResult();
            var query = message.Content as Query;

            queryResult.Result = null;
            queryResult.Type = query.Type;
            queryResult.Exception = exception;

            resultMessage.Sender = MessageReceivers.mediaPrime;
            resultMessage.Receiver = message.Sender;
            resultMessage.Type = MessageTypes.QueryResult;
            resultMessage.Content = queryResult;
            resultMessage.RelatedMessage = message;

            mainWindow.PluginManager.SendMessage(resultMessage);
        }
        public void SendQueryResult(object result, Message message)
        {
            var resultMessage = new Message();
            var queryResult = new QueryResult();
            var query = message.Content as Query;

            queryResult.Result = result;
            queryResult.Type = query.Type;

            resultMessage.Sender = MessageReceivers.mediaPrime;
            resultMessage.Receiver = message.Sender;
            resultMessage.Type = MessageTypes.QueryResult;
            resultMessage.Content = queryResult;
            resultMessage.RelatedMessage = message;

            mainWindow.PluginManager.SendMessage(resultMessage);
        }

        /// <summary>
        /// Waits until condition is true or timeout is reached
        /// </summary>
        /// <param name="condition">Waits until this bool variable is true</param>
        /// <param name="timeout">Timeout in milliseconds</param>
        /// <returns></returns>
        private bool Wait(ref bool condition, int timeout)
        {
            var start = DateTime.Now;

            while (!condition)
            {
                var now = DateTime.Now;
                var elapsedMilliseconds = (now - start).TotalMilliseconds;

                if (elapsedMilliseconds >= timeout)
                    return false;

                System.Threading.Thread.Sleep(10);
            }

            return true;
        }
    }
}
